﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using WooCoo.Client;
using WooCoo.Configuration;
using WooCoo.DataPortal.Client;
using WooCoo.DataPortal.Client;
using WooCoo.ObjectModel;
using WooCoo.Tools;

namespace WooCoo.DataPortal
{
    /// <summary>
    /// 数据门户中介者，该类面对客户端调用者，调用者应根据配置文件设置所采用的客户端代理类
    /// </summary>
    /// <example>
    /// 配置文件<appSettings>节<add key="DataPortalProxy" value="WooCoo.Client.WcfProxy,WooCoo"/>
    /// </example>
    public class DataPortalClient
    {
        //private const string _proxyTypeName = "DataPortalProxy";
        private IDataPortalProxy _mediatorClient;

        public Uri BaseAddress { get { return this.DataPortalProxy.BaseAddress; } }
        public IDataPortalProxy DataPortalProxy { get; private set; }

        private DataPortalClient()
        {
            //string surl = ConfigurationHelper.GetAppSettingOrDefault("DataPortalUrl", "Localhost");
            this.DataPortalProxy = this.GetDataPortalProxy();
            this.DataPortalProxy.BaseAddress = ApplicationContext.DataPortalUrl;//new Uri(surl);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="baseAddress">基地址</param>
        /// <param name="proxy"></param>
        public DataPortalClient(Uri baseAddress, IDataPortalProxy proxy = null)
        {
            if (proxy == null)
            {
                this.DataPortalProxy = this.GetDataPortalProxy();
            }
            this.DataPortalProxy.BaseAddress = baseAddress;
        }
        /// <summary>
        /// 获取ClientMediator类型
        /// </summary>
        /// <param name="forceLocal"></param>
        /// <returns></returns>
        private IDataPortalProxy GetDataPortalProxy(bool forceLocal = false)
        {
            if (forceLocal)
            {
                return new LocalProxy();
            }
            else
            {
                Type _proxyType;
                string proxyTypeName = ApplicationContext.DataPortalProxy;
                if (string.IsNullOrEmpty(proxyTypeName))
                    throw new NullReferenceException(StringID.DataPortalProxyNotSpecified.ToLocalString());
                if (proxyTypeName == "LocalMediator")
                    _proxyType = typeof(LocalProxy);
                else
                    _proxyType = Type.GetType(proxyTypeName, true, true);
                if (_proxyType == null)
                {
                    throw new System.NotSupportedException(proxyTypeName);
                }
                var result = (IDataPortalProxy)Activator.CreateInstance(_proxyType);
                return result;
            }
        }

        private static DataPortalClient _default;
        public static DataPortalClient Default
        {
            get
            {
                if (_default == null)
                {
                    return _default = new DataPortalClient();
                }
                return _default;
            }
        }

        /// <summary>
        /// 处理调用异常的事件处理
        /// </summary>
        public static event EventHandler DataPortalException;

        private static void OnDataPortalException(string operation, Exception ex)
        {
            if (DataPortalException != null)
            {
                DataPortalException(ex, EventArgs.Empty);
            }
            else
            {
                throw ex;
            }
        }

        #region DataPortal methods        

        #region Create
        /// <summary>
        /// 本方法由业务对象的Save()方法中调用,向门户提交业务实例,新增数据.
        /// </summary>
        /// <remarks>
        /// 注意本方法返回更新后的对象引用.如果门户为远程调用,则将会生成一个原实例的副本
        /// 所有的对原对象引用都要指向新生成的对象副本.        
        /// </remarks>
        /// <typeparam name="T">指定的业务对象类型.</typeparam>
        /// <param name="obj">需要更新的业务对象实例.</param>
        /// <returns>返回更新后的业务对象.</returns>
        public void Create<T>(object singleOrList)
        {
            var task = CreateAsync<T>(singleOrList);
            if (task.IsFaulted)
            {
                OnDataPortalException("Create", task.Exception.GetBaseException());
            }
        }

        public Task CreateAsync<T>(object singleOrList)
        {
            //判断类型是否匹配
            Debug.Assert(TypeHelper.GetElementType(singleOrList.GetType()) == typeof(T), "singleOrList != T");
            TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
            //DataPortalResult result = null;
            try
            {
                var result = this.DataPortalProxy.Create<T>(singleOrList);
                tcs.TrySetResult(null);
            }
            catch (Exception ex)
            {
                //OnDataPortalException("Insert", ex);
                tcs.TrySetException(ex);
            }
            return tcs.Task;
        }
        #endregion

        #region Update
        /// <summary>
        /// 本方法由业务对象的Save()方法中调用,向门户提交业务实例,更新数据.
        /// </summary>
        /// <remarks>
        /// 注意本方法返回更新后的对象引用.如果门户为远程调用,则将会生成一个原实例的副本
        /// 所有的对原对象引用都要指向新生成的对象副本.        
        /// </remarks>
        /// <typeparam name="T">指定的业务对象类型.</typeparam>
        /// <param name="obj">需要更新的业务对象实例.</param>
        /// <returns>返回更新后的业务对象.</returns>
        public void Update<T>(object singleOrList)
        {
            var task = UpdateAsync<T>(singleOrList);
            if (task.IsFaulted)
            {
                OnDataPortalException("Update", task.Exception.GetBaseException());
            }
            //UpdateAsync<T>(singleOrList);
        }

        public Task UpdateAsync<T>(object singleOrList)
        {
            TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
            //DataPortalResult result = null;
            try
            {
                var result = this.DataPortalProxy.Update<T>(singleOrList);
                tcs.TrySetResult(null);
            }
            catch (Exception ex)
            {
                //OnDataPortalException("Update", ex);
                tcs.TrySetException(ex);
            }
            return tcs.Task;
        }
        #endregion

        #region Delete
        /// <summary>
        /// 看数据库发送删除请求.        
        /// </summary>
        /// <param name="criteria">Object-specific criteria.</param>
        /// <remarks>请求与Update中的对象中的延迟删除不同.并不传对象实例.而是直接删除请求.
        /// </remarks>
        public void Delete<T>(T entity)
        {
            var task = DeleteAsync<T>(entity);
            if(task.IsFaulted)
            {
                OnDataPortalException("Delete", task.Exception.GetBaseException());
            }
            //    .ContinueWith(task =>
            //{
            //    if (task.IsFaulted)
            //    {
            //        OnDataPortalException("Delete", task.Exception.GetBaseException());
            //    }
            //});
            //DeleteAsync<T>(entity);
        }

        public Task DeleteAsync<T>(T entity)
        {
            TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
            //DataPortalResult result = null;
            try
            {
                if(entity is WooCoo.ObjectModel.Entity && !((entity as Entity).Status ==PersistenceStatus.Deleted))
                {
                    (entity as IEntityOrList).DeleteChild();
                }
                var result = this.DataPortalProxy.Delete<T>(entity);
                tcs.TrySetResult(null);
            }
            catch (Exception ex)
            {
                //OnDataPortalException("Delete", ex);
                tcs.TrySetException(ex);
            }
            return tcs.Task;
        }

        #endregion

        #region Execute

        /// <summary>
        /// 根据Action表达式创建请求
        /// </summary>
        /// <typeparam name="TChannel"></typeparam>
        /// <param name="express"></param>
        /// <returns></returns>
        private static ActionInfo GetRequestMessage<TChannel>(Expression<System.Action<TChannel>> express)
        {
            var ai = new ActionInfo();
            var method = express.Body as MethodCallExpression;
            ai = GetRequestMethod(typeof(TChannel), method);
            return ai;
        }

        /// <summary>
        /// 根据Func表达式创建请求
        /// </summary>
        /// <typeparam name="TChannel"></typeparam>
        /// <typeparam name="TReturn"></typeparam>
        /// <param name="funcEx"></param>
        /// <returns></returns>
        private static ActionInfo GetRequestMessage<TChannel, TReturn>(Expression<System.Func<TChannel, TReturn>> funcEx)
        {
            var ai = new ActionInfo();            
            var method = funcEx.Body as MethodCallExpression;
            ai = GetRequestMethod(typeof(TChannel), method);
            return ai;
        }

        /// <summary>
        /// 封装请求参数
        /// </summary>
        /// <param name="mce"></param>
        /// <returns></returns>
        private static ActionInfo GetRequestMethod(Type type, MethodCallExpression method)
        {
            ActionInfo ai = new ActionInfo();
            ai.ContractName = type.FullName;
            ai.MethodName = method.Method.Name;
            var list = new List<object>();
            for (int i = 0; i < method.Arguments.Count; i++)
            {
                var e = Evaluator.PartialEval(method.Arguments[i]);
                //string key = method.Method.GetParameters()[i].Name;
                if (e is ConstantExpression)
                {
                    //参数值都为常量
                    list.Add((e as ConstantExpression).Value);
                }
            }
            ai.Parameters = list.ToArray();
            return ai;
        }
        /// <summary>
        /// 根据表达式，取得参数值
        /// </summary>
        /// <param name="memberEx"></param>
        /// <returns></returns>
        private static object GetConstantValue(MemberExpression memberEx)
        {
            if (memberEx.Expression is MemberExpression)
            {
                //如果具有子表达式，
                var child = memberEx.Expression as MemberExpression;
                if (memberEx.Member is FieldInfo)
                    return ((FieldInfo)memberEx.Member).GetValue(GetConstantValue(child));
                else
                    return ((PropertyInfo)memberEx.Member).GetValue(GetConstantValue(child), null);
            }
            else if (memberEx.Expression is ConstantExpression)
            {
                var constEx = memberEx.Expression as ConstantExpression;
                return ((FieldInfo)memberEx.Member).GetValue(constEx.Value);
            }
            else
            {
                throw new NotSupportedException("不支持的表达式");
            }
        }

        /// <summary>
        /// 调用方法
        /// </summary>
        /// <typeparam name="TChannel">WCF形式声明的接口类型</typeparam>
        /// <param name="action">接口方法委托</param>
        /// <param name="moduleName">配置模块名</param>
        public void Execute<T>(Expression<System.Action<T>> express)
        {
            var task = ExecuteAsync<T>(express);
            if (task.IsFaulted)
            {
                OnDataPortalException("Execute", task.Exception.GetBaseException());
            }
        }

        public Task ExecuteAsync<T>(Expression<System.Action<T>> express)
        {
            TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
            try
            {
                this.DataPortalProxy.Execute(GetRequestMessage(express));
                tcs.TrySetResult(null);
            }
            catch (Exception ex)
            {
                //OnDataPortalException("Execute", ex);
                tcs.TrySetException(ex);
            }
            return tcs.Task;
        }

        public TReturn Execute<TChannel, TReturn>(Expression<System.Func<TChannel, TReturn>> express)
        {
            var task =  ExecuteAsync<TChannel, TReturn>(express);
            if(task.IsFaulted)
            {
                OnDataPortalException("Execute", task.Exception.GetBaseException());
            }
            return task.Result;
        }

        /// <summary>
        /// 调用方法
        /// </summary>
        /// <typeparam name="TChannel">WCF形式声明的接口类型</typeparam>
        /// <typeparam name="TReturn">返回类型</typeparam>
        /// <param name="func">接口方法委托</param>
        /// <param name="moduleName">配置模块名</param>
        /// <returns>返回TReturn类型</returns>
        public Task<TReturn> ExecuteAsync<TChannel, TReturn>(Expression<System.Func<TChannel, TReturn>> express)
        {
            TaskCompletionSource<TReturn> tcs = new TaskCompletionSource<TReturn>();
            //DataPortalResult result = null;
            try
            {
                var result = this.DataPortalProxy.Execute(GetRequestMessage(express));
                tcs.TrySetResult(this.DataPortalProxy.DeserializeObject<TReturn>(result));
            }
            catch (Exception ex)
            {
                //OnDataPortalException("Execute", ex);
                tcs.TrySetException(ex);
            }
            return tcs.Task;
        }
        #endregion

        #region CreateQuery
        public DataPortalQuery<T> CreateQuery<T>()
        {
            ResourceSetExpression rse = new ResourceSetExpression(typeof(IOrderedQueryable<T>), null, Expression.Constant(typeof(T).Name), typeof(T), null, CountOption.None, null, null, null, null);
            //ResourceSetExpression expression = new ResourceSetExpression(typeof(IOrderedQueryable<T>), null, Expression.Constant(typeof(T).Name), typeof(T), null, CountOption.None, null, null);
            return new DataPortalQuery<T>.DataPortalOrderedQuery(rse, new DataPortalQueryProvider(this.DataPortalProxy));
        }
        #endregion

        #region Security

        private static System.Security.Principal.IPrincipal GetPrincipal()
        {
            if (ApplicationContext.AuthenticationType == "Windows")
            {
                // 集成身份验证时，不传递身份凭据
                return null;
            }
            else
            {
                // 使用框架内的授权
                return ApplicationContext.User;
            }
        }

        #endregion

        #endregion
    }
}
